use crate::document::DocumentManager;
use codegraph_core::{GraphSystem, Result as CoreResult};
use tower_lsp::lsp_types::{
    CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse,
    Documentation, MarkupContent, MarkupKind, Position, InsertTextFormat,
};
use std::sync::Arc;
use std::collections::HashMap;
use std::time::{Duration, Instant};
use tokio::sync::{RwLock, Mutex};
use tracing::{debug, warn};
use dashmap::DashMap;

/// Cache entry for completion results
#[derive(Debug, Clone)]
struct CompletionCacheEntry {
    completions: Vec<CompletionItem>,
    timestamp: Instant,
    context_hash: u64,
}

/// Completion cache with TTL
struct CompletionCache {
    entries: DashMap<String, CompletionCacheEntry>,
    ttl: Duration,
}

impl CompletionCache {
    fn new(ttl: Duration) -> Self {
        Self {
            entries: DashMap::new(),
            ttl,
        }
    }

    fn get(&self, key: &str, context_hash: u64) -> Option<Vec<CompletionItem>> {
        if let Some(entry) = self.entries.get(key) {
            if entry.timestamp.elapsed() < self.ttl && entry.context_hash == context_hash {
                return Some(entry.completions.clone());
            } else {
                // Remove expired entry
                self.entries.remove(key);
            }
        }
        None
    }

    fn insert(&self, key: String, completions: Vec<CompletionItem>, context_hash: u64) {
        let entry = CompletionCacheEntry {
            completions,
            timestamp: Instant::now(),
            context_hash,
        };
        self.entries.insert(key, entry);
    }

    fn clear(&self) {
        self.entries.clear();
    }
}

/// Provides code completion functionality with caching and performance optimization
pub struct CompletionProvider {
    document_manager: Arc<DocumentManager>,
    graph_system: Arc<RwLock<Option<GraphSystem>>>,
    cache: Arc<CompletionCache>,
    // Pre-computed keyword completions for each language
    keyword_cache: Arc<Mutex<HashMap<String, Vec<CompletionItem>>>>,
}

impl CompletionProvider {
    pub fn new(
        document_manager: Arc<DocumentManager>,
        graph_system: Arc<RwLock<Option<GraphSystem>>>,
    ) -> Self {
        let cache = Arc::new(CompletionCache::new(Duration::from_secs(30))); // 30 second TTL
        let keyword_cache = Arc::new(Mutex::new(HashMap::new()));

        Self {
            document_manager,
            graph_system,
            cache,
            keyword_cache,
        }
    }

    pub async fn provide_completion(
        &self,
        params: &CompletionParams,
    ) -> CoreResult<Option<CompletionResponse>> {
        let start_time = Instant::now();
        let uri = &params.text_document_position.text_document.uri;
        let position = params.text_document_position.position;

        debug!("Providing completion for {} at {:?}", uri, position);

        // Get document
        let document = match self.document_manager.get_document(uri) {
            Some(doc) => doc,
            None => {
                warn!("Document not found for completion: {}", uri);
                return Ok(None);
            }
        };

        let (language, context) = {
            let doc = document.read().await;
            let language = match &doc.language {
                Some(lang) => lang.clone(),
                None => {
                    debug!("No language detected for completion");
                    return Ok(None);
                }
            };

            // Get context around the cursor
            let context = self.get_completion_context(&*doc, position)?;
            (language, context)
        };

        // Create cache key
        let cache_key = format!("{}:{}:{}", uri, position.line, position.character);
        let context_hash = self.compute_context_hash(&context);

        // Check cache first
        if let Some(cached_completions) = self.cache.get(&cache_key, context_hash) {
            debug!(
                "Cache hit for completion at {} ({}ms)",
                cache_key,
                start_time.elapsed().as_millis()
            );
            return Ok(Some(CompletionResponse::Array(cached_completions)));
        }

        // Generate completions based on context
        let mut completions = Vec::new();

        // Add keyword completions (cached)
        self.add_keyword_completions_cached(&language, &context, &mut completions).await;

        // Add symbol completions from the graph
        if let Ok(symbol_completions) = self.get_symbol_completions(&language, &context).await {
            completions.extend(symbol_completions);
        }

        // Add snippet completions
        self.add_snippet_completions(&language, &context, &mut completions);

        // Add built-in completions
        self.add_builtin_completions(&language, &context, &mut completions);

        // Sort completions by relevance
        self.sort_completions_by_relevance(&mut completions, &context);

        // Limit the number of completions for performance
        completions.truncate(100);

        // Cache the results
        if !completions.is_empty() {
            self.cache.insert(cache_key.clone(), completions.clone(), context_hash);
        }

        let elapsed = start_time.elapsed();
        debug!(
            "Completion provided for {} in {}ms ({} items)",
            cache_key,
            elapsed.as_millis(),
            completions.len()
        );

        if elapsed > Duration::from_millis(200) {
            warn!(
                "Completion took {}ms (> 200ms threshold) for {}",
                elapsed.as_millis(),
                cache_key
            );
        }

        if completions.is_empty() {
            Ok(None)
        } else {
            Ok(Some(CompletionResponse::Array(completions)))
        }
    }

    fn get_completion_context(&self, doc: &crate::document::Document, position: Position) -> CoreResult<CompletionContext> {
        let offset = doc.position_to_offset(position)
            .map_err(|e| codegraph_core::CodeGraphError::parse_error(e))?;

        // Get the current line
        let line_start = doc.content[..offset].rfind('\n').map(|i| i + 1).unwrap_or(0);
        let line_end = doc.content[offset..].find('\n').map(|i| offset + i).unwrap_or(doc.content.len());
        let current_line = &doc.content[line_start..line_end];

        // Get the word being typed
        let word_start = doc.content[..offset]
            .rfind(|c: char| !c.is_alphanumeric() && c != '_')
            .map(|i| i + 1)
            .unwrap_or(line_start);
        let partial_word = &doc.content[word_start..offset];

        // Determine context type
        let context_type = self.determine_context_type(current_line, partial_word);

        Ok(CompletionContext {
            current_line: current_line.to_string(),
            partial_word: partial_word.to_string(),
            context_type,
            position,
        })
    }

    fn determine_context_type(&self, line: &str, partial_word: &str) -> CompletionContextType {
        let trimmed_line = line.trim();

        if trimmed_line.starts_with("import ") || trimmed_line.starts_with("from ") {
            CompletionContextType::Import
        } else if line.contains('.') && !partial_word.is_empty() {
            CompletionContextType::MemberAccess
        } else if trimmed_line.starts_with("def ") || trimmed_line.starts_with("function ") {
            CompletionContextType::FunctionDefinition
        } else if trimmed_line.starts_with("class ") {
            CompletionContextType::ClassDefinition
        } else {
            CompletionContextType::General
        }
    }

    /// Compute a hash for the completion context to use as cache key
    fn compute_context_hash(&self, context: &CompletionContext) -> u64 {
        use std::collections::hash_map::DefaultHasher;
        use std::hash::{Hash, Hasher};

        let mut hasher = DefaultHasher::new();
        context.current_line.hash(&mut hasher);
        context.partial_word.hash(&mut hasher);
        context.context_type.hash(&mut hasher);
        hasher.finish()
    }

    /// Sort completions by relevance based on context
    fn sort_completions_by_relevance(&self, completions: &mut Vec<CompletionItem>, context: &CompletionContext) {
        completions.sort_by(|a, b| {
            // Prioritize exact prefix matches
            let a_exact = a.label.starts_with(&context.partial_word);
            let b_exact = b.label.starts_with(&context.partial_word);

            match (a_exact, b_exact) {
                (true, false) => std::cmp::Ordering::Less,
                (false, true) => std::cmp::Ordering::Greater,
                _ => {
                    // Then sort by kind priority
                    let a_priority = self.get_completion_kind_priority(a.kind);
                    let b_priority = self.get_completion_kind_priority(b.kind);

                    match a_priority.cmp(&b_priority) {
                        std::cmp::Ordering::Equal => a.label.cmp(&b.label),
                        other => other,
                    }
                }
            }
        });
    }

    /// Get priority for completion item kinds (lower number = higher priority)
    fn get_completion_kind_priority(&self, kind: Option<CompletionItemKind>) -> u8 {
        match kind {
            Some(CompletionItemKind::VARIABLE) => 1,
            Some(CompletionItemKind::FUNCTION) => 2,
            Some(CompletionItemKind::METHOD) => 2,
            Some(CompletionItemKind::CLASS) => 3,
            Some(CompletionItemKind::INTERFACE) => 3,
            Some(CompletionItemKind::KEYWORD) => 4,
            Some(CompletionItemKind::SNIPPET) => 5,
            _ => 10,
        }
    }

    /// Add keyword completions with caching for better performance
    async fn add_keyword_completions_cached(
        &self,
        language: &str,
        context: &CompletionContext,
        completions: &mut Vec<CompletionItem>,
    ) {
        let mut keyword_cache = self.keyword_cache.lock().await;

        // Check if we have cached keyword completions for this language
        if !keyword_cache.contains_key(language) {
            let keyword_completions = self.build_keyword_completions(language);
            keyword_cache.insert(language.to_string(), keyword_completions);
        }

        // Get cached completions and filter by partial word
        if let Some(cached_keywords) = keyword_cache.get(language) {
            for keyword_completion in cached_keywords {
                if keyword_completion.label.starts_with(&context.partial_word) {
                    completions.push(keyword_completion.clone());
                }
            }
        }
    }

    /// Build keyword completions for a specific language
    fn build_keyword_completions(&self, language: &str) -> Vec<CompletionItem> {
        let keywords = match language {
            "python" => vec![
                "def", "class", "if", "elif", "else", "for", "while", "try", "except", "finally",
                "import", "from", "as", "return", "yield", "break", "continue", "pass", "lambda",
                "and", "or", "not", "in", "is", "True", "False", "None",
            ],
            "javascript" | "typescript" => vec![
                "function", "class", "if", "else", "for", "while", "do", "switch", "case", "default",
                "try", "catch", "finally", "return", "break", "continue", "var", "let", "const",
                "import", "export", "from", "as", "async", "await", "true", "false", "null", "undefined",
            ],
            "rust" => vec![
                "fn", "struct", "enum", "impl", "trait", "if", "else", "match", "for", "while", "loop",
                "let", "mut", "const", "static", "use", "mod", "pub", "return", "break", "continue",
                "true", "false", "Some", "None", "Ok", "Err",
            ],
            _ => vec![],
        };

        keywords
            .into_iter()
            .map(|keyword| CompletionItem {
                label: keyword.to_string(),
                kind: Some(CompletionItemKind::KEYWORD),
                detail: Some(format!("{} keyword", language)),
                documentation: Some(Documentation::MarkupContent(MarkupContent {
                    kind: MarkupKind::Markdown,
                    value: format!("**{}** keyword in {}", keyword, language),
                })),
                insert_text: Some(keyword.to_string()),
                ..Default::default()
            })
            .collect()
    }

    /// Legacy method for backward compatibility
    fn add_keyword_completions(
        &self,
        language: &str,
        context: &CompletionContext,
        completions: &mut Vec<CompletionItem>,
    ) {
        let keyword_completions = self.build_keyword_completions(language);
        for keyword_completion in keyword_completions {
            if keyword_completion.label.starts_with(&context.partial_word) {
                completions.push(keyword_completion);
            }
        }
    }

    async fn get_symbol_completions(
        &self,
        _language: &str,
        context: &CompletionContext,
    ) -> CoreResult<Vec<CompletionItem>> {
        let mut completions = Vec::new();

        // Get the graph system
        let graph_system = self.graph_system.read().await;
        let _graph_system = match graph_system.as_ref() {
            Some(system) => system,
            None => {
                debug!("Graph system not available for symbol completion");
                return Ok(completions);
            }
        };

        // TODO: Query the graph for relevant symbols
        // This would include:
        // - Functions and methods in scope
        // - Classes and types
        // - Variables and constants
        // - Imported symbols

        // For now, add some mock completions
        if context.partial_word.starts_with("test") {
            completions.push(CompletionItem {
                label: "test_function".to_string(),
                kind: Some(CompletionItemKind::FUNCTION),
                detail: Some("function test_function() -> None".to_string()),
                documentation: Some(Documentation::MarkupContent(MarkupContent {
                    kind: MarkupKind::Markdown,
                    value: "A test function from the code graph".to_string(),
                })),
                insert_text: Some("test_function()".to_string()),
                ..Default::default()
            });
        }

        Ok(completions)
    }

    fn add_snippet_completions(
        &self,
        language: &str,
        context: &CompletionContext,
        completions: &mut Vec<CompletionItem>,
    ) {
        match language {
            "python" => {
                if context.partial_word.starts_with("def") || context.context_type == CompletionContextType::FunctionDefinition {
                    completions.push(CompletionItem {
                        label: "def function".to_string(),
                        kind: Some(CompletionItemKind::SNIPPET),
                        detail: Some("Function definition".to_string()),
                        insert_text: Some("def ${1:function_name}(${2:args}):\n    ${3:pass}".to_string()),
                        insert_text_format: Some(InsertTextFormat::SNIPPET),
                        ..Default::default()
                    });
                }

                if context.partial_word.starts_with("class") || context.context_type == CompletionContextType::ClassDefinition {
                    completions.push(CompletionItem {
                        label: "class definition".to_string(),
                        kind: Some(CompletionItemKind::SNIPPET),
                        detail: Some("Class definition".to_string()),
                        insert_text: Some("class ${1:ClassName}:\n    def __init__(self${2:, args}):\n        ${3:pass}".to_string()),
                        insert_text_format: Some(InsertTextFormat::SNIPPET),
                        ..Default::default()
                    });
                }
            }
            "javascript" | "typescript" => {
                if context.partial_word.starts_with("func") {
                    completions.push(CompletionItem {
                        label: "function".to_string(),
                        kind: Some(CompletionItemKind::SNIPPET),
                        detail: Some("Function definition".to_string()),
                        insert_text: Some("function ${1:functionName}(${2:args}) {\n    ${3:// body}\n}".to_string()),
                        insert_text_format: Some(InsertTextFormat::SNIPPET),
                        ..Default::default()
                    });
                }

                if context.partial_word.starts_with("class") {
                    completions.push(CompletionItem {
                        label: "class".to_string(),
                        kind: Some(CompletionItemKind::SNIPPET),
                        detail: Some("Class definition".to_string()),
                        insert_text: Some("class ${1:ClassName} {\n    constructor(${2:args}) {\n        ${3:// constructor body}\n    }\n}".to_string()),
                        insert_text_format: Some(InsertTextFormat::SNIPPET),
                        ..Default::default()
                    });
                }
            }
            "rust" => {
                if context.partial_word.starts_with("fn") {
                    completions.push(CompletionItem {
                        label: "fn function".to_string(),
                        kind: Some(CompletionItemKind::SNIPPET),
                        detail: Some("Function definition".to_string()),
                        insert_text: Some("fn ${1:function_name}(${2:args}) ${3:-> ReturnType} {\n    ${4:// body}\n}".to_string()),
                        insert_text_format: Some(InsertTextFormat::SNIPPET),
                        ..Default::default()
                    });
                }

                if context.partial_word.starts_with("struct") {
                    completions.push(CompletionItem {
                        label: "struct".to_string(),
                        kind: Some(CompletionItemKind::SNIPPET),
                        detail: Some("Struct definition".to_string()),
                        insert_text: Some("struct ${1:StructName} {\n    ${2:field}: ${3:Type},\n}".to_string()),
                        insert_text_format: Some(InsertTextFormat::SNIPPET),
                        ..Default::default()
                    });
                }
            }
            _ => {}
        }
    }

    fn add_builtin_completions(
        &self,
        language: &str,
        context: &CompletionContext,
        completions: &mut Vec<CompletionItem>,
    ) {
        match language {
            "python" => {
                let builtins = vec![
                    ("print", "function", "print(*args, **kwargs) -> None"),
                    ("len", "function", "len(obj) -> int"),
                    ("str", "class", "str(object='') -> str"),
                    ("int", "class", "int(x=0) -> int"),
                    ("list", "class", "list(iterable=()) -> list"),
                    ("dict", "class", "dict(**kwargs) -> dict"),
                ];

                for (name, kind_str, signature) in builtins {
                    if name.starts_with(&context.partial_word) {
                        let kind = match kind_str {
                            "function" => CompletionItemKind::FUNCTION,
                            "class" => CompletionItemKind::CLASS,
                            _ => CompletionItemKind::VALUE,
                        };

                        completions.push(CompletionItem {
                            label: name.to_string(),
                            kind: Some(kind),
                            detail: Some(signature.to_string()),
                            documentation: Some(Documentation::MarkupContent(MarkupContent {
                                kind: MarkupKind::Markdown,
                                value: format!("Built-in {} `{}`", kind_str, signature),
                            })),
                            insert_text: Some(name.to_string()),
                            ..Default::default()
                        });
                    }
                }
            }
            "javascript" | "typescript" => {
                let builtins = vec![
                    ("console", "object", "console object for logging"),
                    ("Array", "class", "Array constructor"),
                    ("Object", "class", "Object constructor"),
                    ("String", "class", "String constructor"),
                    ("Number", "class", "Number constructor"),
                    ("Boolean", "class", "Boolean constructor"),
                ];

                for (name, kind_str, description) in builtins {
                    if name.starts_with(&context.partial_word) {
                        let kind = match kind_str {
                            "function" => CompletionItemKind::FUNCTION,
                            "class" => CompletionItemKind::CLASS,
                            "object" => CompletionItemKind::MODULE,
                            _ => CompletionItemKind::VALUE,
                        };

                        completions.push(CompletionItem {
                            label: name.to_string(),
                            kind: Some(kind),
                            detail: Some(description.to_string()),
                            insert_text: Some(name.to_string()),
                            ..Default::default()
                        });
                    }
                }
            }
            _ => {}
        }
    }
}

#[derive(Debug, Clone)]
struct CompletionContext {
    current_line: String,
    partial_word: String,
    context_type: CompletionContextType,
    position: Position,
}

#[derive(Debug, Clone, PartialEq, Hash)]
enum CompletionContextType {
    General,
    Import,
    MemberAccess,
    FunctionDefinition,
    ClassDefinition,
}
